using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using System.IO;
using TelnetExpect;

namespace TelnetTests
{
    [TestFixture]
    public class TelnetXStream_Read_Tests_Offset1 : TelnetXStream_Read_Tests
    {
        protected override void Create_ResultBuffer(int maxLen, out byte[] result, out int offset)
        {
            const int Offset = 1;
            int offsetBase;
            base.Create_ResultBuffer(maxLen + Offset, out result, out offsetBase);
            offset = Offset + offsetBase;
        }
    }

    [TestFixture]
    public class TelnetXStream_Read_Tests_Offset100 : TelnetXStream_Read_Tests
    {
        protected override void Create_ResultBuffer(int maxLen, out byte[] result, out int offset)
        {
            const int Offset = 100;
            int offsetBase;
            base.Create_ResultBuffer(maxLen + Offset, out result, out offsetBase);
            offset = Offset + offsetBase;
        }
    }

    [TestFixture]
    public class TelnetXStream_Read_Tests_OneByteAtATime : TelnetXStream_Read_Tests
    {
        protected override Stream Modifier_CreateStream(Stream src)
        {
            return new OneByteAtATimeStream(src);
        }

    }

    [TestFixture]
    public partial class TelnetXStream_Read_Tests : TestsBase
    {
        //--------
        [Test]
        public void Clear()
        {
            byte[] input = { 1, 2, 3, 4, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesNone);
        }

        [Test]
        public void Byte255()
        {
            byte[] input = { 1, 2, 3, IAC, 255, 4, 5 };
            DoTest(input, ExpectedInputByte255At3, ExpectedResponsesNone);
        }

        [Test]
        public void Byte255_AfterCommandBy1()
        {
            byte[] input = { 1, 2, IAC, DO, OptionFooOne, 3, IAC, 255, 4, 5 };
            DoTest(input, ExpectedInputByte255At3, ExpectedResponsesOne);
        }

        [Test]
        public void Byte255_AfterCommandBy0()
        {
            byte[] input = { 1, 2, 3, IAC, DO, OptionFooOne, IAC, 255, 4, 5 };
            DoTest(input, ExpectedInputByte255At3, ExpectedResponsesOne);
        }

        //--------
        [Test]
        public void Option_AtMiddle()
        {
            byte[] input = { 1, 2, IAC, DO, OptionFooOne, 3, 4, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesOne);
        }

        [Test]
        public void Option_AtFront()
        {
            byte[] input = { IAC, DO, OptionFooOne, 1, 2, 3, 4, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesOne);
        }

        [Test]
        public void Option_AtEnd()
        {
            byte[] input = { 1, 2, 3, 4, 5, IAC, DO, OptionFooOne, };
            DoTest(input, ExpectedInput, ExpectedResponsesOne);
        }

        [Test]
        public void Option_All()
        {
            byte[] expected = { };
            byte[] input = { IAC, DO, OptionFooOne, };
            DoTest(input, expected);
        }

        //--------
        [Test]
        public void Option_TwoTogether()
        {
            byte[] input = { 1, 2, IAC, DO, OptionFooOne, IAC, DO, OptionFooTwo, 3, 4, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesTwo);
        }

        [Test]
        public void Option_Two_OneApart()
        {
            byte[] input = { 1, 2, IAC, DO, OptionFooOne, 3, IAC, DO, OptionFooTwo, 4, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesTwo);
        }

        [Test]
        public void Option_Two_TwoApart()
        {
            byte[] input = { 1, 2, IAC, DO, OptionFooOne, 3, 4, IAC, DO, OptionFooTwo, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesTwo);
        }

        //--------
        // Command (non-(sub-)negotiation), e.g. AYT

        //TODO Check correct byte is passed to HandleCommand

        [Test]
        public void CommandAyt()
        {
            byte[] input = { 1, 2, IAC, AYT, 3, 4, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesNone);
        }

        [Test]
        public void CommandNop()
        {
            byte[] input = { 1, 2, IAC, NOP, 3, 4, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesNone);
        }

        [Test]
        public void CommandUndefinedLowValue()
        {
            byte[] input = { 1, 2, IAC, 55, 3, 4, 5 };
            DoTest(input, ExpectedInput, ExpectedResponsesNone);
        }


        //--------
        // Sub-Negotiation of course only occurs after agreement on the main option,
        // so these would have to be preceded by the main option negotiation.

        [Test]
        public void SubOptionOne()
        {
            byte[] input = { 1, 2, IAC, SB, 9, 10, 11, IAC, SE, 3, 4, 5 };
            byte[] ExpectedSubNegOne = { 9, 10, 11 };
            DoTest(input, ExpectedInput, null,
                null, null, null,
                ExpectedSubNegOne);
        }

        [Test]
        public void SubOptionNawsWithSize255()
        {
            const byte NAWS = 31;
            byte[] input = { 1, 2, IAC, SB, NAWS, /*w=80*/0, 80, /*h=255*/0, 255, 255, IAC, SE, 3, 4, 5 };
            byte[] ExpectedSubNegNawsWithSize255 = { NAWS, /*w=80*/0, 80, /*h=255*/0, 255 };
            DoTest(input, ExpectedInput, null,
                null, null, null,
                ExpectedSubNegNawsWithSize255);
        }

        [Test]
        public void SubOptionNawsWithSize255255()
        {
            const byte NAWS = 31;
            byte[] input = { 1, 2, IAC, SB, NAWS, /*w=80*/0, 80, /*h=65535*/255,255, 255, 255, IAC, SE, 3, 4, 5 };
            byte[] ExpectedSubNegNawsWithSize255 = { NAWS, /*w=80*/0, 80, /*h=65535*/255, 255 };
            DoTest(input, ExpectedInput, null,
                null, null, null,
                ExpectedSubNegNawsWithSize255);
        }

        [Test]
        public void SubOptionWithUnescaped255()
        {
            const byte NAWS = 31;
            byte[] input = { 1, 2, IAC, SB, NAWS, /*w=255 !!!*/0, 255, /*h=24*/0, 24, IAC, SE, 3, 4, 5 };
            byte[] ExpectedSubNegNawsWithSize255 = { NAWS, /*w=80*/0, 80, /*h=65535*/255, 255 };
            try {
                DoTest(input, ExpectedInput, null,
                    null, null, null,
                    ExpectedSubNegNawsWithSize255);
                Assert.Fail("should have thrown!");
            } catch (NotImplementedException) {
            }
        }

        [Test]
        public void SubOptionOneThenNegAt0()
        {
            byte[] input = { 1, 2, IAC, SB, 9, 10, 11, IAC, SE, IAC, DO, OptionFooOne, 3, 4, 5 };
            byte[] ExpectedSubNegOne = { 9, 10, 11 };
            DoTest(input, ExpectedInput, ExpectedResponsesOne,
                null, null, null,
                ExpectedSubNegOne);
        }

        [Test]
        public void SubOptionEmpty()
        {
            byte[] input = { 1, 2, IAC, SB, IAC, SE, 3, 4, 5 };
            byte[] ExpectedSubNegEmpty = { };
            DoTest(input, ExpectedInput, null,
                null, null, null,
                ExpectedSubNegEmpty);
        }

    }
}
